/*
 * Copyright (c) 2016.
 * kupats(sz)
 * www.kuparts.com.
 * Created By chenbin on 16-5-20 下午2:45.
 */

package com.md.common.utils.redis;

import redis.clients.jedis.JedisPubSub;
import redis.clients.jedis.exceptions.JedisException;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 功能描述：redis操作接口
 *
 * @author lujianping@oak168.com
 * @version 1.0
 * @date 2018-6-15 11:51
 * @since JDK 1.8
 */
@SuppressWarnings("unused")
public interface RedisOperations {

    /**
     * 获取一个key的当前失效时间
     *
     * @param key key
     * @return 失效时间 秒
     */
    long ttl(String key);

    /**
     * get value from redis
     *
     * @param key 键
     * @return 返回值byte[]
     */
    byte[] get(String key);

    /**
     * set
     *
     * @param key   键
     * @param value 值
     */
    void set(String key, byte[] value);

    /**
     * sadd
     *
     * @param key 键
     * @param members 值
     */
    void sadd(String key, String... members);

    /**
     * sget
     *
     * @param key 键
     * @return 值
     */
    Set<String> sget(String key);

    /**
     * sremove
     *
     * @param key 键
     */
    void sremove(String key, String... members);

    /**
     * set
     *
     * @param key    键
     * @param value  值
     * @param expire 过期时间
     */
    void set(String key, byte[] value, int expire);

    /**
     * 检查给定 key 是否存在。
     *
     * @param key 键
     */
    boolean exists(String key);

    /**
     * 设置key的过期时间
     *
     * @param key     键
     * @param seconds 秒数
     */
    Long setExpire(String key, int seconds);

    Long llen(String key);

    String lset(String key, long index, String value);

    /**
     * 对一个列表进行修剪(trim)，就是说，让列表只保留指定区间内的元素，不在指定区间之内的元素都将被删除。
     * 举个例子，执行命令 LTRIM list 0 2 ，表示只保留列表 list 的前三个元素，其余元素全部删除。
     * 下标(index)参数 start 和 stop 都以 0 为底，也就是说，以 0 表示列表的第一个元素，以 1 表示列表的第二个元素，以此类推。
     * 你也可以使用负数下标，以 -1 表示列表的最后一个元素， -2 表示列表的倒数第二个元素，以此类推
     *
     * @param key   列表
     * @param start 开始下标,从0开始
     * @param end   结束下标,闭区间
     * @return 如果 key 不是列表类型，返回一个错误。
     */
    String ltrim(String key, long start, long end);

    /**
     * 返回列表 key 中，下标为 index 的元素。
     * <p>
     * 下标(index)参数 start 和 stop 都以 0 为底，也就是说，以 0 表示列表的第一个元素，以 1 表示列表的第二个元素，以此类推。
     * <p>
     * 你也可以使用负数下标，以 -1 表示列表的最后一个元素， -2 表示列表的倒数第二个元素，以此类推。
     * <p>
     *
     * @param key   列表
     * @param index 下标,从0开始
     * @return 如果 key 不是列表类型，返回一个错误。
     */
    String lindex(String key, long index);

    /**
     * 删除列表中的指定键和值,并返回删除成功的数量
     * 参考http://redisdoc.com/list/lrem.html
     *
     * @param key   键
     * @param count 要删除的数量
     * @param value 值
     * @return 返回删除成功的数量
     */
    Long lrem(String key, int count, String value);

    /**
     * 左进列表
     * 参考http://redisdoc.com/list/lpush.html
     *
     * @param key   键
     * @param value 值
     * @return 命令执行之后，表的长度。
     */
    Long lpush(String key, String value);

    /**
     * 右进列表
     * 参考http://redisdoc.com/list/rpush.html
     *
     * @param key   键
     * @param value 值
     * @return 命令执行之后，表的长度。
     */
    Long rpush(String key, String value);

    /**
     * 右出列表
     * 参考http://redisdoc.com/list/rpop.html
     *
     * @param key 键
     * @return 返回值
     */
    String rpop(String key);

    /**
     * 左出列表
     * 参考http://redisdoc.com/list/lpop.html
     *
     * @param key 键
     * @return 返回值
     */
    String lpop(String key);

    /**
     * 返回列表 key 中指定区间内的元素，区间以偏移量 start 和 stop 指定。
     * 下标(index)参数 start 和 stop 都以 0 为底，也就是说，以 0 表示列表的第一个元素，以 1 表示列表的第二个元素，以此类推。
     * 你也可以使用负数下标，以 -1 表示列表的最后一个元素， -2 表示列表的倒数第二个元素，以此类推。
     * 超出范围的下标值不会引起错误。
     * 如果 start 下标比列表的最大下标 stop ( LLEN list 减去 1 )还要大，那么 LRANGE 返回一个空列表。
     * <p>
     * 如果 stop 下标比 stop 下标还要大，Redis将 stop 的值设置为 stop 。
     * 参考http://redisdoc.com/list/lrange.html
     *
     * @param key   键
     * @param start 开始偏移量
     * @param stop  结果偏移量
     * @return 返回成功数量
     */
    List<String> lrange(String key, long start, long stop);

    /**
     * 将 key 的值设为 value ，当且仅当 key 不存在。 若给定的 key 已经存在，则 SETNX 不做任何动作。
     *
     * @param key       键
     * @param value     值
     * @param expireSec 过期 秒
     * @return 成功返回true, 失败或已经存在的返回false
     */
    boolean setnx(String key, byte[] value, int expireSec);

    /**
     * 将 key 的值设为 value ，当且仅当 key 不存在。 若给定的 key 已经存在，则 SETNX 不做任何动作。
     *
     * @param key       键
     * @param value     值
     * @param expireSec 过期 秒
     * @return 成功返回true, 失败或已经存在的返回false
     */
    boolean setnx(String key, String value, int expireSec);


    /**
     * 获取序列化对象
     *
     * @param key   key
     * @param clazz class
     * @param <T> 值的泛型
     * @return 对象
     */
    <T> T getObject(String key, Class<T> clazz);

    /**
     * 保存key和object
     *
     * @param key 键
     */
    void setObject(String key, Object object);

    /**
     * 保存key和object
     *
     * @param key    键
     * @param object 对象
     * @param expire 过期时间（秒）
     */
    void setObject(String key, Object object, int expire);

    /**
     * 通过key获取String类型的value
     *
     * @param key 键
     * @return String
     */
    String getString(String key);

    /**
     * 保存String的key和value
     *
     * @param key   键
     * @param value 值
     */
    void setString(String key, String value);

    /**
     * 保存String的key和value
     * 支持过期时间设置
     *
     * @param key    键
     * @param value  值
     * @param expire 过期时间（秒）
     */
    void setString(String key, String value, int expire);

    /**
     * del
     *
     * @param key 键
     */
    Long del(String key);

    /**
     * dels
     *
     * @param keys 多个键
     */
    Long del(String... keys);

    /**
     * 模糊匹配key
     *
     * @param pattern 模式
     * @return Set<String>
     */
    Set<String> keys(String pattern);

    /**
     * BLPOP 是列表的阻塞式(blocking)弹出原语。
     * <p>
     * 它是 LPOP 命令的阻塞版本，当给定列表内没有任何元素可供弹出的时候，连接将被 BLPOP 命令阻塞，直到等待超时或发现可弹出元素为止。
     * <p>
     * 当给定多个 key 参数时，按参数 key 的先后顺序依次检查各个列表，弹出第一个非空列表的头元素。
     * <p>
     * 非阻塞行为
     * <p>
     * 当 BLPOP 被调用时，如果给定 key 内至少有一个非空列表，那么弹出遇到的第一个非空列表的头元素，并和被弹出元素所属的列表的名字一起，组成结果返回给调用者。
     * <p>
     * 当存在多个给定 key 时， BLPOP 按给定 key 参数排列的先后顺序，依次检查各个列表。
     * <p>
     * 假设现在有 job 、 command 和 request 三个列表，其中 job 不存在， command 和 request 都持有非空列表。考虑以下命令：
     * <p>
     * BLPOP job command request 0
     * <p>
     * BLPOP 保证返回的元素来自 command ，因为它是按”查找 job -> 查找 command -> 查找 request “这样的顺序，第一个找到的非空列表。
     *
     * @param timeout 接受一个以秒为单位的数字作为值。超时参数设为 0 表示阻塞时间可以无限期延长(block indefinitely)
     * @param key     相同的 key 可以被多个客户端同时阻塞。不同的客户端被放进一个队列中，按『先阻塞先服务』的顺序为 key 执行 BLPOP 命令。
     *                当给定多个 key 参数时，按参数 key 的先后顺序依次检查各个列表，弹出第一个非空列表的头元素。
     * @return 返回列表数据
     */
    List<String> blpop(int timeout, String key);

    /**
     * BRPOP 是列表的阻塞式(blocking)弹出原语。
     * <p>
     * 它是 RPOP 命令的阻塞版本，当给定列表内没有任何元素可供弹出的时候，连接将被 BRPOP 命令阻塞，直到等待超时或发现可弹出元素为止。
     * <p>
     * 当给定多个 key 参数时，按参数 key 的先后顺序依次检查各个列表，弹出第一个非空列表的尾部元素。
     * <p>
     * 关于阻塞操作的更多信息，请查看 BLPOP 命令， BRPOP 除了弹出元素的位置和 BLPOP 不同之外，其他表现一致。
     *
     * @param timeout 接受一个以秒为单位的数字作为值。超时参数设为 0 表示阻塞时间可以无限期延长(block indefinitely)
     * @param key     相同的 key 可以被多个客户端同时阻塞。不同的客户端被放进一个队列中，按『先阻塞先服务』的顺序为 key 执行 BLPOP 命令。
     *                当给定多个 key 参数时，按参数 key 的先后顺序依次检查各个列表，弹出第一个非空列表的头元素。
     * @return 假如在指定时间内没有任何元素被弹出，则返回一个 nil 和等待时长。
     * 反之，返回一个含有两个元素的列表，第一个元素是被弹出元素所属的 key ，第二个元素是被弹出元素的值。
     */
    List<String> brpop(final int timeout, final String key);

    /**
     * BRPOPLPUSH 是 RPOPLPUSH 的阻塞版本，当给定列表 source 不为空时， BRPOPLPUSH 的表现和 RPOPLPUSH 一样。
     * <p>
     * 当列表 source 为空时， BRPOPLPUSH 命令将阻塞连接，直到等待超时，或有另一个客户端对 source 执行 LPUSH 或 RPUSH 命令为止。
     * <p>
     * 超时参数 timeout 接受一个以秒为单位的数字作为值。超时参数设为 0 表示阻塞时间可以无限期延长(block indefinitely) 。
     * <p>
     * 更多相关信息，请参考 RPOPLPUSH 命令。
     *
     * @param source      源队列
     * @param destination 目标队列, 如果 source 和 destination 相同，则列表中的表尾元素被移动到表头，并返回该元素，可以把这种特殊情况视作列表的旋转(rotation)操作
     * @param timeout     超时时间,为0秒时,表示不过期
     * @return 假如在指定时间内没有任何元素被弹出，则返回一个 nil 和等待时长。
     * 反之，返回一个含有两个元素的列表，第一个元素是被弹出元素的值，第二个元素是等待时长。
     */
    String brpoplpush(String source, String destination, int timeout);

    String rpoplpush(String source, String destination);

    Double zscore(final String key, final String member);

    Long zadd(final String key, final double score, final String member);

    Long zadd(final String key, final Map<String, Double> scoreMembers);

    Set<String> zrange(final String key, final long start, final long end);

    Set<String> zrevrange(final String key, final long start, final long end);

    Long zrem(final String key, final String... members);

    Double zincrby(final String key, final double score, final String member);

    Set<String> zrangeByScore(final String key, final double min, final double max, final int offset, final int count);

    Set<String> zrangeByScore(final String key, final double min, final double max);

    Set<String> zrevrangeByScore(final String key, final double max, final double min);

    Set<String> zrevrangeByScore(final String key, final double max, final double min, final int offset, final int count);

    Long zremrangeByScore(final String key, final double start, final double end);

    Long rpushx(final String key, final String value);

    Long lpushx(final String key, final String value);

    Long zremrangeByRank(final String key, final long start, final long end);

    Long zcount(final String key, final double min, final double max);

    /**
     * 此方法会阻塞当前线程
     *
     * @param jedisPubSub 订阅监听器对象
     * @param channels    订阅的渠道,支持多个
     */
    void subscribe(final JedisPubSub jedisPubSub, final String... channels);

    /**
     * 订阅一个或多个符合给定模式的频道。
     * <p>
     * 每个模式以 * 作为匹配符，比如 it* 匹配所有以 it 开头的频道( it.news 、 it.blog 、 it.tweets 等等)，
     * news.* 匹配所有以 news. 开头的频道( news.it 、 news.global.today 等等)，诸如此类
     * 时间复杂度：
     * O(N)， N 是订阅的模式的数量。。
     *
     * @param jedisPubSub 订阅监听器对象
     * @param patterns    订阅的模式数组
     */
    void psubscribe(final JedisPubSub jedisPubSub, final String... patterns);

    /**
     * 发布消息给指定渠道,正在监听该渠道的监听器会收到该消息,非持久化消息
     *
     * @param channel 要发布的渠道,支持多个
     * @param message 消息
     * @return 返回成功1,
     */
    Long publish(final String channel, final String message);


    /**
     * 将 key 中储存的数字值增一。
     * <p>
     * 如果 key 不存在，那么 key 的值会先被初始化为 0 ，然后再执行 INCR 操作。
     * <p>
     * 如果值包含错误的类型，或字符串类型的值不能表示为数字，那么返回一个错误。
     * <p>
     * 本操作的值限制在 64 位(bit)有符号数字表示之内。
     *
     * @param key key
     * @return 执行 INCR 命令之后 key 的值。
     * @throws JedisException 如果指定key获取到的值不是整数类型,则会抛出异常
     */
    Long incr(String key) throws JedisException;

    /**
     * 将 key 中储存的数字值增increment 。
     * <p>
     * 如果 key 不存在，那么 key 的值会先被初始化为 0 ，然后再执行 INCR 操作。
     * <p>
     * 如果值包含错误的类型，或字符串类型的值不能表示为数字，那么返回一个错误。
     * <p>
     * 本操作的值限制在 64 位(bit)有符号数字表示之内。
     *
     * @param key       key
     * @param increment 增量
     * @return 加上 increment 之后， key 的值。
     * @throws JedisException 如果指定key获取到的值不是整数类型,则会抛出异常
     */
    Long incrBy(String key, int increment) throws JedisException;

    /**
     * 为 key 中所储存的值加上浮点数增量 increment 。
     * <p>
     * 如果 key 不存在，那么 key 的值会先被初始化为 0 ，然后再执行 INCR 操作。
     * <p>
     * 如果命令执行成功，那么 key 的值会被更新为（执行加法之后的）新值，并且新值会以字符串的形式返回给调用者。
     * 无论是 key 的值，还是增量 increment ，都可以使用像 2.0e7 、 3e5 、 90e-2 那样的指数符号(exponential notation)来表示，
     * 但是，执行 INCRBYFLOAT 命令之后的值总是以同样的形式储存，也即是，它们总是由一个数字，一个（可选的）小数点和一个任意位
     * 的小数部分组成（比如 3.14 、 69.768 ，诸如此类)，小数部分尾随的 0 会被移除，如果有需要的话，还会将浮点数改为整数（比如 3.0 会被保存成 3 ）。
     * 除此之外，无论加法计算所得的浮点数的实际精度有多长， INCRBYFLOAT 的计算结果也最多只能表示小数点的后十七位。
     *
     * @param key       key
     * @param increment 增量
     * @return 加上 increment 之后， key 的值。
     * @throws JedisException 如果指定key获取到的值不是整数类型,则会抛出异常
     */
    Double incrByFloat(String key, double increment) throws JedisException;

    /**
     * 将 key 中储存的数字值减一。
     * <p>
     * 如果 key 不存在，那么 key 的值会先被初始化为 0 ，然后再执行 DECR 操作。
     * <p>
     * 如果值包含错误的类型，或字符串类型的值不能表示为数字，那么返回一个错误。
     * <p>
     * 本操作的值限制在 64 位(bit)有符号数字表示之内。
     *
     * @param key key
     * @return 执行 DECR 命令之后 key 的值。
     * @throws JedisException 如果指定key获取到的值不是整数类型,则会抛出异常
     */
    Long decr(String key) throws JedisException;

    /**
     * 将 key 中储存的数字值减increment 。
     * <p>
     * 如果 key 不存在，那么 key 的值会先被初始化为 0 ，然后再执行 DECR 操作。
     * <p>
     * 如果值包含错误的类型，或字符串类型的值不能表示为数字，那么返回一个错误。
     * <p>
     * 本操作的值限制在 64 位(bit)有符号数字表示之内。
     *
     * @param key       key
     * @param decrement 增量
     * @return 减 increment 之后， key 的值。
     * @throws JedisException 如果指定key获取到的值不是整数类型,则会抛出异常
     */
    Long decrBy(String key, int decrement) throws JedisException;

    /**
     * 返回哈希表所有的值
     *
     * @param key 键
     * @return 值
     */
    Map<String, String> hgetAll(String key);

    /**
     * 返回哈希表所有的key值
     *
     * @param key 键
     */
    Set<String> hkeys(String key);

    /**
     * HGET key field
     * <p>
     * 返回哈希表 key 中给定域 field 的值。
     *
     * @param key   key
     * @param field field
     * @return 给定域的值。
     * 当给定域不存在或是给定 key 不存在时，返回 nil 。
     */
    String hget(String key, String field);

    /**
     * HGET key field
     *
     * @param key   key
     * @param field field
     * @return 返回给定key 与 fiedl返回 byte 数组
     */
    byte[] hgetBytes(String key, String field);

    /**
     * HMGET key field [field ...]
     * <p>
     * 返回哈希表 key 中，一个或多个给定域的值。
     * <p>
     * 如果给定的域不存在于哈希表，那么返回一个 nil 值。
     * <p>
     * 因为不存在的 key 被当作一个空哈希表来处理，所以对一个不存在的 key 进行 HMGET 操作将返回一个只带有 nil 值的表。
     * 时间复杂度： O(N)， N 为给定域的数量。
     *
     * @param key    key
     * @param fields fields
     * @return 一个包含多个给定域的关联值的表，表值的排列顺序和给定域参数的请求顺序一样。
     */
    List<String> hmget(String key, String... fields);

    /**
     * hIncrBy key field value 有上限和下限
     * @param key  键
     * @param field 字段
     * @param value 值
     * @param max 最大值
     * @param min 最小值
     * @return -1表示超过max,-3低于min,-2表示添加失败(在设置时,被修改),0表示field已经存在  其他则为正常返回
     */
    Long hIncrBy(String key, String field, Integer value, Integer max, Integer min);
    /**
     * HSET key field value
     * <p>
     * 将哈希表 key 中的域 field 的值设为 value 。
     * <p>
     * 如果 key 不存在，一个新的哈希表被创建并进行 HSET 操作。
     * <p>
     * 如果域 field 已经存在于哈希表中，旧值将被覆盖。
     * 时间复杂度： O(N)， N 为给定域的数量。
     *
     * @param key   key
     * @param field field
     * @param value value
     * @return 如果 field 是哈希表中的一个新建域，并且值设置成功，返回 1 。
     * 如果哈希表中域 field 已经存在且旧值已被新值覆盖，返回 0 。
     */
    Long hset(String key, String field, String value);

    /**
     * 将一个byte 数组数据加入到map集合中
     *
     * @param key   key
     * @param field field
     * @param value value
     * @return 如果 field 是哈希表中的一个新建域，并且值设置成功，返回 1 。
     * 如果哈希表中域 field 已经存在且旧值已被新值覆盖，返回 0 。
     */
    long hsetBytes(String key, String field, byte[] value);

    /**
     * HMSET key field value [field value ...]
     * <p>
     * 同时将多个 field-value (域-值)对设置到哈希表 key 中。
     * <p>
     * 此命令会覆盖哈希表中已存在的域。
     * <p>
     * 如果 key 不存在，一个空哈希表被创建并执行 HMSET 操作
     *
     * @param key key
     * @param kv  kv
     * @return 如果命令执行成功，返回 OK 。
     * 当 key 不是哈希表(hash)类型时，返回一个错误。
     */
    String hmset(String key, Map<String, String> kv);

    /**
     * HINCRBY key field increment
     * <p>
     * 为哈希表 key 中的域 field 的值加上增量 increment 。
     * <p>
     * 增量也可以为负数，相当于对给定域进行减法操作。
     * <p>
     * 如果 key 不存在，一个新的哈希表被创建并执行 HINCRBY 命令。
     * <p>
     * 如果域 field 不存在，那么在执行命令前，域的值被初始化为 0 。
     * <p>
     * 对一个储存字符串值的域 field 执行 HINCRBY 命令将造成一个错误。
     * <p>
     * 本操作的值被限制在 64 位(bit)有符号数字表示之内。
     *
     * @param key       key
     * @param field     field
     * @param increment increment
     * @return 执行 HINCRBY 命令之后，哈希表 key 中域 field 的值。
     */
    Long hIncrBy(String key, String field, int increment);

    /**
     * HSETNX key field value
     * <p>
     * 将哈希表 key 中的域 field 的值设置为 value ，当且仅当域 field 不存在。
     * <p>
     * 若域 field 已经存在，该操作无效。
     * <p>
     * 如果 key 不存在，一个新哈希表被创建并执行 HSETNX 命令。
     *
     * @param key   key
     * @param field field
     * @param value value
     * @return 设置成功，返回true 。如果给定域已经存在且没有操作被执行，返回 false
     */
    boolean hsetnx(String key, String field, String value);

    /**
     * HEXISTS key field
     * <p>
     * 查看哈希表 key 中，给定域 field 是否存在。
     *
     * @param key   key
     * @param field field
     * @return 如果哈希表含有给定域，返回 1 。
     * 如果哈希表不含有给定域，或 key 不存在，返回 0 。
     */
    boolean hexists(String key, String field);

    /**
     * HDEL key field [field ...]
     * <p>
     * 删除哈希表 key 中的一个或多个指定域，不存在的域将被忽略。
     *
     * @param key    key
     * @param fields fields
     * @return 被成功移除的域的数量，不包括被忽略的域。
     */
    Long hdel(String key, String... fields);

    /**
     * HLEN key
     * <p>
     * 返回哈希表 key 中域的数量。
     *
     * @param key key
     * @return 哈希表中域的数量。
     * 当 key 不存在时，返回 0 。
     */
    Long hlen(String key);

    String getAndSet(String key, String value);
}
